﻿using System;
using System.Collections.Generic;

namespace Framework.Core.Queue
{
    public class BaseStatusQueue<TKey, TValue, TStatus> : IStatusQueue<TKey, TValue, TStatus>
    {
        private IDictionary<TKey, IStatusQueuer<TKey, TValue, TStatus>> queuers;
        private object syncObj;

        #region IStatusQueue<TKey,TValue,TStatus> Members

        public event NewQueuerInQueueHandler<TKey, TValue, TStatus> OnNewQueuerInQueue;

        public event QueuerOutQueueHandler<TKey, TValue, TStatus> OnQueuerOutQueue;

        public object SyncObj
        {
            get
            {
                if (Equals(syncObj, null))
                {
                    syncObj = new object();
                }
                return syncObj;
            }
            set { syncObj = value; }
        }

        public IDictionary<TKey, IStatusQueuer<TKey, TValue, TStatus>> Queuers
        {
            get
            {
                if (Equals(queuers, null))
                {
                    queuers = new Dictionary<TKey, IStatusQueuer<TKey, TValue, TStatus>>();
                }
                return queuers;
            }
        }

        public virtual IStatusQueuer<TKey, TValue, TStatus> Add(TKey key, TValue value, TStatus status)
        {
            if (Equals(queuers, null))
            {
                queuers = new Dictionary<TKey, IStatusQueuer<TKey, TValue, TStatus>>();
            }

            if (!queuers.ContainsKey(key))
            {
                lock (SyncObj)
                {
                    if (!queuers.ContainsKey(key))
                    {
                        IStatusQueuer<TKey, TValue, TStatus> queuer = new BaseStatusQueuer<TKey, TValue, TStatus>(key,
                                                                                                                  value,
                                                                                                                  status);
                        queuers.Add(key, queuer);
                        queuer.OnInQueuer();
                        if (!Equals(OnNewQueuerInQueue, null))
                        {
                            OnNewQueuerInQueue(this, queuer);
                        }
                        return queuer;
                    }
                }
            }
            return null;
        }

        public virtual IStatusQueuer<TKey, TValue, TStatus> Add(IStatusQueuer<TKey, TValue, TStatus> queuer)
        {
            if (Equals(queuers, null))
            {
                queuers = new Dictionary<TKey, IStatusQueuer<TKey, TValue, TStatus>>();
            }

            if (Equals(queuer, null))
            {
                return null;
            }

            if (!queuers.ContainsKey(queuer.Key))
            {
                lock (SyncObj)
                {
                    if (!queuers.ContainsKey(queuer.Key))
                    {
                        queuers.Add(queuer.Key, queuer);
                        queuer.OnInQueuer();
                        if (!Equals(OnNewQueuerInQueue, null))
                        {
                            OnNewQueuerInQueue(this, queuer);
                        }
                        return queuer;
                    }
                }
            }
            return null;
        }

        public virtual bool ContainsKey(TKey key)
        {
            lock (SyncObj)
            {
                return Queuers.ContainsKey(key);
            }
        }

        public virtual bool Remove(TKey key)
        {
            if (queuers.ContainsKey(key))
            {
                lock (SyncObj)
                {
                    if (queuers.ContainsKey(key))
                    {
                        IStatusQueuer<TKey, TValue, TStatus> queue = queuers[key];
                        bool returnValue = queuers.Remove(key);
                        if (returnValue)
                        {
                            if (!Equals(OnQueuerOutQueue, null))
                            {
                                {
                                    OnQueuerOutQueue(this, queue);
                                }
                            }
                        }
                        return returnValue;
                    }
                }
            }
            return false;
        }

        public virtual IStatusQueuer<TKey, TValue, TStatus> this[TKey key]
        {
            get
            {
                lock (SyncObj)
                {
                    return Queuers[key];
                }
            }
        }

        #endregion

        #region GetTopNQueuers

        /// <summary>
        /// 
        /// </summary>
        /// <param name="count"></param>
        /// <returns></returns>
        public virtual IList<IStatusQueuer<TKey, TValue, TStatus>> GetTopNQueuers(int count)
        {
            IList<IStatusQueuer<TKey, TValue, TStatus>> list = new List<IStatusQueuer<TKey, TValue, TStatus>>();

            if (Queuers.Count <= 0
                || (count <= 0 && count != -1))
            {
                return list;
            }

            int i = 0;
            lock (SyncObj)
            {
                foreach (TKey key in Queuers.Keys)
                {
                    IStatusQueuer<TKey, TValue, TStatus> myQueuer = Queuers[key];

                    list.Add(myQueuer);
                    i++;

                    if (count == -1 && i >= count)
                    {
                        break;
                    }
                }
            }

            return list;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="count"></param>
        /// <param name="statuses"></param>
        /// <returns></returns>
        public virtual IList<IStatusQueuer<TKey, TValue, TStatus>> GetTopNQueuers(int count, IList<TStatus> statuses)
        {
            IList<IStatusQueuer<TKey, TValue, TStatus>> list = new List<IStatusQueuer<TKey, TValue, TStatus>>();

            if (Queuers.Count <= 0
                || (count <= 0 && count != -1)
                || Equals(statuses, null)
                || statuses.Count <= 0)
            {
                return list;
            }

            int i = 0;
            lock (SyncObj)
            {
                foreach (TKey key in Queuers.Keys)
                {
                    IStatusQueuer<TKey, TValue, TStatus> myQueuer = Queuers[key];
                    if (statuses.Contains(myQueuer.Status))
                    {
                        list.Add(myQueuer);
                        i++;

                        if (count == -1 && i >= count)
                        {
                            break;
                        }
                    }
                }
            }

            return list;
        }

        /// <summary>
        /// 得到一定数量、在某些状态范围中、最后访问时间大于某一间隔的前N个队列成员
        /// </summary>
        /// <param name="count"></param>
        /// <param name="statuses"></param>
        /// <param name="lastQueryTimeSpan"></param>
        /// <returns></returns>
        public virtual IList<IStatusQueuer<TKey, TValue, TStatus>> GetTopNQueuers(int count, IList<TStatus> statuses,
                                                                                  TimeSpan lastQueryTimeSpan)
        {
            IList<IStatusQueuer<TKey, TValue, TStatus>> list = new List<IStatusQueuer<TKey, TValue, TStatus>>();

            if (Queuers.Count <= 0
                || (count <= 0 && count != -1)
                || Equals(statuses, null)
                || statuses.Count <= 0)
            {
                return list;
            }

            int i = 0;
            lock (SyncObj)
            {
                foreach (TKey key in Queuers.Keys)
                {
                    IStatusQueuer<TKey, TValue, TStatus> myQueuer = Queuers[key];
                    if ((DateTime.Now - myQueuer.LastQueryTime) > lastQueryTimeSpan &&
                        statuses.Contains(myQueuer.Status))
                    {
                        list.Add(myQueuer);
                        i++;

                        if (count == -1 && i >= count)
                        {
                            break;
                        }
                    }
                }
            }

            return list;
        }

        #endregion

        #region GetTimeOutQueuers

        public virtual IList<IStatusQueuer<TKey, TValue, TStatus>> GetTimeOutQueuers(TimeSpan timeOutTimeSpan)
        {
            IList<IStatusQueuer<TKey, TValue, TStatus>> list = new List<IStatusQueuer<TKey, TValue, TStatus>>();

            if (Queuers.Count <= 0)
            {
                return list;
            }

            lock (SyncObj)
            {
                foreach (TKey key in Queuers.Keys)
                {
                    IStatusQueuer<TKey, TValue, TStatus> myQueuer = Queuers[key];
                    if ((DateTime.Now - myQueuer.InQueueTime) > timeOutTimeSpan)
                    {
                        list.Add(myQueuer);
                    }
                }
            }

            return list;
        }

        public virtual IList<IStatusQueuer<TKey, TValue, TStatus>> GetTimeOutQueuers(int count, TimeSpan timeOutTimeSpan)
        {
            IList<IStatusQueuer<TKey, TValue, TStatus>> list = new List<IStatusQueuer<TKey, TValue, TStatus>>();

            if (Queuers.Count <= 0
                || (count <= 0 && count != -1))
            {
                return list;
            }

            int i = 0;
            lock (SyncObj)
            {
                foreach (TKey key in Queuers.Keys)
                {
                    IStatusQueuer<TKey, TValue, TStatus> myQueuer = Queuers[key];
                    if ((DateTime.Now - myQueuer.InQueueTime) > timeOutTimeSpan)
                    {
                        list.Add(myQueuer);
                        i++;

                        if (count == -1 && i >= count)
                        {
                            break;
                        }
                    }
                }
            }

            return list;
        }

        #endregion
    }
}